﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.CSS.SAC.Selectors
{
    #region Using Statements

    using KLF.Lib.Browser.DOM.HTML;
    using System.Collections.Generic;
    using KLF.Lib.Browser.DOM.CSS.SAC.Conditions;
    using System;

    #endregion

    /// <summary>
    /// 
    /// </summary>
    public enum SelectorType
    {
        Unknown = 0,
        ConditionalSelector = 1,
        AnyNodeSelector = 2,
        RootNodeSelector = 3,
        NegativeSelector = 4,
        ElementNodeSelector = 5,
        TextNodeSelector = 6,
        CDataSectionNodeSelector = 7,
        ProcessingInstructionNodeSelector = 8,
        CommentNodeSelector = 9,
        PseudoElementSelector = 10,
        DescendantSelector = 11,
        ChildSelector = 12,
        DirectAdjacentSelector = 13
    }

    /// <summary>
    /// 
    /// </summary>
    public abstract class Selector
    {
        #region Constructors

        public Selector()
        {
            Conditions = new List<Condition>();
        }

        #endregion

        #region Attributes

        public List<Condition> Conditions { get; set; }

        public virtual SelectorType Type { get { return SelectorType.Unknown; } }

        #endregion

        #region Methods

        public Selector AddCondition(Condition c)
        {
            Conditions.Add(c);
            return this;
        }

        public Selector ByAttribute(string attName, AttOp attOp, string attVal)
        {
            return AddCondition(new AttributeCondition(attName, attOp, attVal));
        }

        public Selector ByClass(string spec)
        {
            return AddCondition(new ClassCondition(spec));
        }

        public Selector ByElementName(string spec)
        {
            if (this is ElementSelector)
            {
                (this as ElementSelector).ElementName = spec.ToLowerInvariant();
                return this;
            }
            else
            {
                return new AndSelector(
                    new Selector[] 
                    {
                        this,
                        new ElementSelector() { ElementName = spec } 
                    });
            }
        }

        public Selector ById(string spec)
        {
            return AddCondition(new IdCondition(spec));
        }

        public Selector ByName(string spec)
        {
            return AddCondition(new AttributeCondition(HTMLConstants.HTML_ATT_NAME, "=", spec));
        }

        public Selector BySelector(string spec)
        {
            SACParser p = new SACParser();
            return new AndSelector(
                new Selector[] 
                    {
                        this,
                        p.ParseSelector(spec)
                    });
        }

        public abstract bool Matches(HTMLElement e);

        #endregion
    }
}
